package com.xueliman.iov.auth_server_resource_common.config;


import com.xueliman.iov.auth_server_resource_common.other.ApiException;
import com.xueliman.iov.auth_server_resource_common.other.Group;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * 处理Controller层抛出的异常
 *
 * @author yjw
 * @date 2021/02/23
 **/
@Component
@Aspect
@Slf4j
public class WebLogAop {

//    @Pointcut("execution(public * com.xueliman.iov.*.client.*Feign.*(..))")
//    public void feignCutPoint() {
//    }
//
//    @Around("feignCutPoint()")
//    public Object handlerFeign(ProceedingJoinPoint pjp) {
//        long startTime = System.currentTimeMillis();
//        SingleResultBundle<?> result;
//        try {
//            result = (SingleResultBundle<?>) pjp.proceed();
//            if (!result.isSuccess()){
//                throw new ApiException(result.getMessage());
//            }
//        } catch (ApiException e) {
//            throw new ApiException(e.getMessage());
//        }catch (Throwable e) {
//            result = handlerException(pjp, e);
//        }
//        return result;
//    }

    /**
     * 定义切点
     */
    @Pointcut("execution(public * com.xueliman.iov.*.controller.*Controller.*(..))")
    public void controllerParams() {
    }

    //验证器工厂
    private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    private static final ExecutableValidator methodValidator = factory.getValidator().forExecutables();
    //验证器
    private static final Validator beanValidator = factory.getValidator();

    /**
     * 参数验证
     */
    @Before("controllerParams()")
    public void beforeLog(JoinPoint point) {
        Object[] args = point.getArgs();
        Object target = point.getThis();

        //此处可以抛个异常提示用户参数输入格式不正确
        MethodSignature joinPointObject = (MethodSignature) point.getSignature();//可以获取到目标方法名,所属类的Class等信息
        Method method = joinPointObject.getMethod();
        validMethodParams(target, method, args).forEach(aValidResult -> {
            throw new ApiException(aValidResult.getMessage());
        });

        //此处判断注解参数
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if (parameterAnnotations != null && parameterAnnotations.length != 0) {
            for (int i = 0; i < parameterAnnotations.length; i++) {
                // 获取参数bean
                Object arg = args[i];
                Annotation[] parameterAnnotation = parameterAnnotations[i];
                // 交给group校验
                boolean hasGroup = isHasGroup(arg, parameterAnnotation);
                // 交给bean校验
                noGroupValid(arg, hasGroup);
            }
        }
    }

    private void noGroupValid(Object arg, boolean hasGroup) {
        if (!hasGroup && arg != null) {
            validBeanParams(arg).forEach(aValidResult -> {
                String message = aValidResult.getMessage();
                throw new ApiException(message);
            });
        }
    }

    private boolean isHasGroup(Object arg, Annotation[] parameterAnnotation) {
        for (Annotation annotation : parameterAnnotation) {
            if (annotation instanceof Group) {
                Group group = (Group) annotation;
                Class value = group.value();
                validBeanParamsGroup(arg, value).forEach(aValidResult -> {
                    throw new ApiException(aValidResult.getMessage());
                });
                return true;
            }
        }
        return false;
    }

    private <T> Set<ConstraintViolation<T>> validBeanParams(T bean) {
        return beanValidator.validate(bean);
    }

    private <T> Set<ConstraintViolation<T>> validBeanParamsGroup(T bean, Class... group) {
        return beanValidator.validate(bean, group);
    }

    private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object[] params) {
        return methodValidator.validateParameters(obj, method, params);
    }

}
